package com.bensonbasement.snorflux.server;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.net.InetSocketAddress;

import org.apache.mina.core.service.IoAcceptor;
import org.apache.mina.core.service.IoHandlerAdapter;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.filter.codec.ProtocolCodecFilter;
import org.apache.mina.filter.codec.serialization.ObjectSerializationCodecFactory;
import org.apache.mina.transport.socket.nio.NioSocketAcceptor;

import com.bensonbasement.snorflux.messages.fromclient.IFromClientMessage;

public class SnorfluxServerHandler extends IoHandlerAdapter {
	
	public static final int DEFAULT_PORT = 9123;
	
	public static void main(String[] args) throws IOException {
		SnorfluxServerHandler server = new SnorfluxServerHandler(DEFAULT_PORT,null);
		server.startServer();
	}
	
	private final IoAcceptor acceptor;
	private final int port;
	private final Game game;
	
	public SnorfluxServerHandler(int port) {
		this(port,null);
	}
	
	public SnorfluxServerHandler(int port, File saveFile) {
		// TODO: Figure out where to load this stuff:
		ContentRepository.getInstance().loadTestContent();
		if (saveFile != null) {
			FileInputStream fis = null;
			ObjectInputStream in = null;
			try
			{
				fis = new FileInputStream(saveFile);
				in = new ObjectInputStream(fis);
				game = (Game)in.readObject();
				in.close();
			}
			// TODO: Handle these exceptions more appropriately.
			catch(IOException ex)
			{
				throw new RuntimeException("Error loading file.",ex);
			}
			catch(ClassNotFoundException ex)
			{
				throw new RuntimeException("Class not found!",ex);
			}
		}
		else {
			// TODO: Add option for shared sight in GUI.
			this.game = new Game(false);
		}
		
		this.port = port;
		this.acceptor = new NioSocketAcceptor();
		this.acceptor.getFilterChain().addLast("codec", new ProtocolCodecFilter(new ObjectSerializationCodecFactory()));
		this.acceptor.setHandler(this);
	}
	
	public void startServer() {
		try {
			this.acceptor.bind(new InetSocketAddress(port));
		}
		catch (IOException ex) {
			// TODO: Do something more appropriate here.
			// This is where the "port in use" exception stems from.
			throw new RuntimeException(ex);
		}
	}
	
	public void stopServer() {
		for (IoSession session : this.acceptor.getManagedSessions().values()) {
			session.close(false);
		}
		this.acceptor.dispose();
	}
	
	@Override
	public synchronized void exceptionCaught(IoSession session, Throwable cause)
			throws Exception {
		// TODO: Handle this more intelligently.
		cause.printStackTrace();
		session.close(true);
	}
	
	@Override
	public synchronized void messageReceived(IoSession session, Object message)
			throws Exception {
		game.handleMessage(session, (IFromClientMessage) message);
	}
	
	@Override
	public synchronized void sessionClosed(IoSession session) throws Exception {
		game.playerDisconnected(session);
	}
	
	public synchronized void startGame() {
		this.game.startGame();
	}
	
	public synchronized void saveGame(File file) {
		this.game.saveGame(file);
		
	}
	
	public boolean isGameRunning() {
		return game.isRunning();
	}
}
